
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "input.h"
#include "str.h"
#include "lex.h"
#include "expr.h"
#include "output.h"
#define LOG_TAG "Output"
#include "log.h"

#define OutputLabelNum 1024
#define NO_LINK_SRC

#define AssertNodeKind(node, k) \
    if ((node)->kind != NK_##k) { \
        LOGE("%s(%d): node->kind=%d, but ask NK_%s(%d)", __func__, __LINE__, (node)->kind, #k, NK_##k); \
        exit(EXT_NodeKindErr); \
    } \
    Ast##k t##k = (Ast##k) (node)

typedef struct {
    const char *type;
    const char *name;
} GlobalVariable;

static FILE *CSourceFile = NULL;
static FILE *CHeaderFile = NULL;
static const char *OutputFunction[OutputLabelNum];
static int OutputFunctionCount = 0;
static GlobalVariable OutputVariable[OutputLabelNum];
static int OutputVariableCount = 0;

const char *Indent = "    "; // "\t"
char OutputEnumDefine = 0;

static void PrintTypeFunction(AstNode node);
static void PrintTypeDefine(AstNode node, int level, FILE *fp);

#define SPrint(fmt, ...) fprintf(CSourceFile, fmt, ##__VA_ARGS__)
#define HPrint(fmt, ...) fprintf(CHeaderFile, fmt, ##__VA_ARGS__)

static void OpenOutputFile(const char *outPath, const char *name)
{
    // 打开输出C源文件
    int nameLen = (int) (strlen(outPath) + strlen(name)) + 1;
    char *path = (char*) malloc(nameLen + 5);
    if (path == NULL) {
        LOGE("Memory exhausted");
        exit(EXT_MemoryFull);
    }
    strcpy(path, outPath);
    strcat(path, "\\");
    strcat(path, name);
    path[nameLen] = '.';
    path[nameLen + 1] = 'c';
    path[nameLen + 2] = '\0';
    CSourceFile = fopen(path, "wb");
    if (CSourceFile == NULL) {
        LOGE("%s: %s", strerror(errno), path);
        exit(EXT_FileOpenFail);
    }

    // 输出公共文件头
    SPrint("#include \"StreamType.h\"\n");
    SPrint("#include \"%s.h\"\n", name);
    #ifndef NO_LINK_SRC
    SPrint("\n#line 0 \"%s\"\n", Input.fileName);
    #endif

    // 打开输出C头文件
    path[nameLen + 1] = 'h';
    CHeaderFile = fopen(path, "wb");
    if (CHeaderFile == NULL) {
        LOGE("%s: %s", strerror(errno), path);
        exit(EXT_FileOpenFail);
    }

    // 头文件避免重复包含
    int i;
    path[0] = '_';
    nameLen = (int) strlen(name);
    for (i = 0; i < nameLen; i++) {
        if (name[i] >= 'a' && name[i] <= 'z') {
            path[i + 1] = name[i] - 'a' + 'A';
        } else if ((name[i] >= 'A' && name[i] <= 'Z') ||
            (name[i] >= '0' && name[i] <= '9')) {
            path[i + 1] = name[i];
        } else {
            path[i + 1] = '_';
        }
    }
    path[nameLen + 1] = '_';
    path[nameLen + 2] = 'H';
    path[nameLen + 3] = '_';
    path[nameLen + 4] = '\0';
    HPrint("#ifndef %s\n", path);
    HPrint("#define %s\n", path);
    free(path);

    // 输出公共文件头
    HPrint("\n#pragma once\n");
    HPrint("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
}

static void CloseOuputFile()
{
    // 输出公共文件尾
    HPrint("\n#ifdef __cplusplus\n}\n#endif\n");
    HPrint("\n#endif\n");

    fclose(CSourceFile);
    fclose(CHeaderFile);
}

static const char *GetTypeString(AstDeclaration decl, int useOftLen)
{
    static const char *baseTypeStr[] = {
        NULL, "char", "unsigned char", "signed char",
        "unsigned short", "signed short", "unsigned int", "signed int",
        "unsigned long long", "signed long long", "float", "double"
    };
    int base = decl->baseType & 0xFF;
    const char *type = NULL;
    if (useOftLen && decl->baseType & (BT_StaticArray | BT_DynArray)) {
        type = "OftLen";
    } else if (base >= BT_Bit && base <= BT_Double) {
        type = baseTypeStr[base];
    } else if (base == BT_Typedef) {
        AssertNodeKind(decl->type, Label);
        type = tLabel->name;
    } else {
        AssertNodeKind(decl->type, Expression);
        type = tExpression->funcName;
    }
    if (type == NULL) {
        LOGE("Unknow base type(%d)", decl->baseType);
        type = "void";
    }
    return type;
}

static void AddOutputFunction(const char *name)
{
    int i;
    for (i = 0; i < OutputFunctionCount; i++) {
        if (OutputFunction[i] == name) {
            return;
        }
    }

    if (OutputFunctionCount >= OutputLabelNum) {
        LOGE("OutputFunction fill");
        exit(EXT_OutputBufferFull);
    }
    OutputFunction[OutputFunctionCount] = name;
    OutputFunctionCount++;
}

static void AddOutputVariable(const char *name, AstDeclaration decl)
{
    int i;
    for (i = 0; i < OutputVariableCount; i++) {
        if (OutputVariable[i].name == name) {
            return;
        }
    }

    if (OutputVariableCount >= OutputLabelNum) {
        LOGE("OutputVariable fill");
        exit(EXT_OutputBufferFull);
    }
    OutputVariable[OutputVariableCount].name = name;
    OutputVariable[OutputVariableCount].type = GetTypeString(decl, 1);
    OutputVariableCount++;
}

static void PrintLineNumber(int line, FILE *fp)
{
    #ifndef NO_LINK_SRC
    fprintf(fp, "#line %d\n", line);
    #endif
}

void PrintIndent(int count, FILE *fp)
{
    int i;
    for (i = 0; i < count; i++)
        fprintf(fp, Indent);
}

/***************************************************************/

static void PrintEnumDefine(AstExpression node, int level, FILE *fp)
{
    fprintf(fp, "typedef enum {\n");
    level++;

    AstNode p = node->kids[1];
    while (p != NULL) {
        AssertNodeKind(p, EnumDeclaration);
        PrintIndent(level, fp);
        fprintf(fp, "%s = %d", tEnumDeclaration->name, tEnumDeclaration->val);

        p = p->next;
        if (p != NULL) fprintf(fp, ",\n");
        else fprintf(fp, "\n");
    }

    level--;
    PrintIndent(level, fp);
    fprintf(fp, "} %s;\n", node->funcName);
}

static void PrintEnumFunction(AstExpression node, AstDeclaration decl)
{
    if (node->funcName == NULL) {
        node->funcName = Print2Buf("EnumFunc%d", SoleIndex++);
    } else if (OutputEnumDefine) {
        PrintEnumDefine(node, 0, CHeaderFile);
    }

    const char *type = GetTypeString(decl, 0);
    SPrint("void Pause%s(%s *val, char type) {\n", node->funcName, type);
    SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
    SPrint("%s%s valTmp;\n", Indent, type);
    SPrint("%sif (val == NULL)\n", Indent);
    SPrint("%s%sval = &valTmp;\n", Indent, Indent);
    SPrint("%s*val = ReadNumber();\n", Indent);
    SPrint("%sif (type != T_Print) return;\n", Indent);

    SPrint("%s#else // ifndef ReadAndPrintOnly\n", Indent);
    SPrint("%sCheckInput(val);\n", Indent);
    SPrint("%sif (type != T_Print) {\n", Indent);
    SPrint("%s%sPauseNumber(val, type);\n", Indent, Indent);
    SPrint("%s%sreturn;\n", Indent, Indent);
    SPrint("%s}\n", Indent);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    // 打印函数
    SPrint("%sswitch (*val) {\n", Indent);

    AstNode p = node->kids[1];
    while (p != NULL) {
        AssertNodeKind(p, EnumDeclaration);
        SPrint("%s%scase %d: ", Indent, Indent, tEnumDeclaration->val);
        SPrint("PrintLabelValue(\"%s\", T_Print); break;\n", tEnumDeclaration->name);

        p = p->next;
    }
    SPrint("%s%sdefault: ", Indent, Indent);
    SPrint("PrintLabelValue(Number2Str(*val), T_Print); break;\n");

    SPrint("%s}\n}\n", Indent);
}

static void PrintFieldFunction(AstExpression node, AstDeclaration decl)
{
    if (node->funcName == NULL) {
        node->funcName = Print2Buf("FieldFunc%d", SoleIndex++);
    }

    const char *type = GetTypeString(decl, 0);
    SPrint("void Pause%s(%s *val, int type) {\n", node->funcName, type);
    SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
    SPrint("%s%s val0 = ReadNumber();\n", Indent, type);
    SPrint("%sif (val != NULL)\n", Indent);
    SPrint("%s%s*val = val0;\n", Indent, Indent);
    SPrint("%sif (type != T_Print) return;\n", Indent);

    SPrint("%s#else // ifndef ReadAndPrintOnly\n", Indent);
    SPrint("%sCheckInput(val);\n", Indent);
    SPrint("%sif (type != T_Print) {\n", Indent);
    SPrint("%s%sPauseNumber(val, type);\n", Indent, Indent);
    SPrint("%s%sreturn;\n", Indent, Indent);
    SPrint("%s}\n", Indent);
    SPrint("%s%s val0 = *val;\n", Indent, type);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    // 打印函数
    SPrint("%schar valBuf[LABEL_VALUE_MAX_LEN] = \"\";\n", Indent);

    AstNode p = node->kids[1];
    while (p != NULL) {
        AssertNodeKind(p, EnumDeclaration);
        SPrint("%sif (BitCheck(val0, %d)) {\n", Indent, tEnumDeclaration->val);
        SPrint("%s%sBitClear(val0, %d);\n", Indent, Indent, tEnumDeclaration->val);
        SPrint("%s%sif (valBuf[0]) strcat(valBuf, \" | \");\n", Indent, Indent);
        SPrint("%s%sstrcat(valBuf, \"%s\");\n", Indent, Indent, tEnumDeclaration->name);
        SPrint("%s}\n", Indent);

        p = p->next;
    }
    SPrint("%sif (val0 != 0) {\n", Indent);
    SPrint("%s%sif (valBuf[0]) strcat(valBuf, \" | \");\n", Indent, Indent);
    SPrint("%s%sstrcat(valBuf, Number2Str(val0));\n", Indent, Indent);
    SPrint("%s}\n", Indent);

    SPrint("%sPrintLabelValue(valBuf, T_Print);\n", Indent);
    SPrint("}\n");
}

static void PrintStructDefine(AstExpression node, int level, FILE *fp)
{
    fprintf(fp, "struct {\n");

    level++;
    AstNode p = node->kids[1];
    while (p != NULL) {
        PrintIndent(level, fp);
        PrintTypeDefine(p, level, fp);
        p = p->next;
    }
    level--;
    PrintIndent(level, fp);
    fprintf(fp, "} ");
}

static void PrintStructFunction(AstExpression node, AstDeclaration decl)
{
    AstNode p = node->kids[1];
    while (p != NULL) {
        PrintTypeFunction(p);
        p = p->next;
    }
    const char *type;
    if (node->funcName == NULL) {
        node->funcName = Print2Buf("StructFunc%d", SoleIndex++);
        if (decl != NULL && decl->funcName != NULL) {
            type = decl->funcName;
        } else {
            SPrint("typedef ");
            PrintStructDefine(node, 0, CSourceFile);
            SPrint("%s;\n", node->funcName);
            type = node->funcName;
        }
    } else {
        type = node->funcName;
    }

    if (type == decl->funcName && (decl->baseType & (BT_StaticArray | BT_DynArray))) {
        SPrint("void Pause%s(%s val, char type) {\n", node->funcName, type);
    } else {
        SPrint("void Pause%s(%s *val, char type) {\n", node->funcName, type);
    }
    SPrint("%s#ifndef ReadAndPrintOnly\n", Indent);
    SPrint("%sCheckInput(val);\n", Indent);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);

    SPrint("%sPrintLevelAdd(type);\n", Indent);
    p = node->kids[1];
    while (p != NULL) {
        AssertNodeKind(p, Declaration);
        SPrint("%sPrintLabelKey(\"%s\", type);\n", Indent, tDeclaration->name);
        SPrint("%sPause%s((val != NULL) ? &(val->%s) : NULL, type);\n",
            Indent, tDeclaration->funcName, tDeclaration->name);
        SPrint("%sPrintLabelEndKey(type);\n", Indent);
        p = p->next;
    }
    SPrint("%sPrintLevelSub(type);\n", Indent);

    SPrint("}\n");
}

static void PrintChoseDefine(AstExpression node, int level, FILE *fp)
{
    fprintf(fp, "union {\n");

    level++;
    AstNode p = node->kids[1];
    while (p != NULL) {
        AssertNodeKind(p, ChoseDeclaration);
        PrintIndent(level, fp);
        PrintTypeDefine(tChoseDeclaration->expr, level, fp);
        p = p->next;
    }
    level--;
    PrintIndent(level, fp);
    fprintf(fp, "} ");
}

static void PrintChoseFunction(AstExpression node, AstDeclaration decl)
{
    AstNode p = node->kids[1];
    while (p != NULL) {
        AssertNodeKind(p, ChoseDeclaration);
        PrintTypeFunction(tChoseDeclaration->expr);
        p = p->next;
    }
    const char *type;
    if (node->funcName == NULL) {
        node->funcName = Print2Buf("ChoseFunc%d", SoleIndex++);
        if (decl != NULL && decl->funcName != NULL) {
            type = decl->funcName;
        } else {
            SPrint("typedef ");
            PrintChoseDefine(node, 0, CSourceFile);
            SPrint("%s;\n", node->funcName);
            type = node->funcName;
        }
    } else {
        type = node->funcName;
    }

    if (type == decl->funcName && (decl->baseType & (BT_StaticArray | BT_DynArray))) {
        SPrint("void Pause%s(%s val, char type) {\n", node->funcName, type);
    } else {
        SPrint("void Pause%s(%s *val, char type) {\n", node->funcName, type);
    }
    SPrint("%s#ifndef ReadAndPrintOnly\n", Indent);
    SPrint("%sCheckInput(val);\n", Indent);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);

    AssertNodeKind(node->kids[0], Literal);
    SPrint("%sswitch (%.*s) {\n", Indent, tLiteral->valLen, tLiteral->val);
    p = node->kids[1];
    char hasDef = 0;
    while (p != NULL) {
        AssertNodeKind(p, ChoseDeclaration);
        AssertNodeKind(tChoseDeclaration->expr, Declaration);
        if (tChoseDeclaration->isDefault) {
            SPrint("%s%sdefault: ", Indent, Indent);
            hasDef = 1;
        } else {
            SPrint("%s%scase %d: ", Indent, Indent, tChoseDeclaration->val);
        }
        SPrint("Pause%s((val != NULL) ? &(val->%s) : NULL, type); ",
            tDeclaration->funcName, tDeclaration->name);
        SPrint("break;\n");
        p = p->next;
    }
    if (!hasDef) {
        SPrint("%s%sdefault: ThrowError(__LINE__); break;\n", Indent, Indent);
    }
    SPrint("%s}\n", Indent);

    SPrint("}\n");
}

static void PrintTypeDefine(AstNode node, int level, FILE *fp)
{
    AssertNodeKind(node, Declaration);

    switch (tDeclaration->baseType & 0xFF) {
    case BT_Bit:
    case BT_UChar:
    case BT_SChar:
    case BT_UShort:
    case BT_SShort:
    case BT_UInt:
    case BT_SInt:
    case BT_ULong:
    case BT_SLong:
    case BT_Float:
    case BT_Double:
    case BT_Typedef:
        fprintf(fp, "%s ", GetTypeString(tDeclaration, 0));
        break;
    case BT_Struct:
        PrintStructDefine((AstExpression) tDeclaration->type, level, fp);
        break;
    case BT_Union:
        PrintChoseDefine((AstExpression) tDeclaration->type, level, fp);
        break;
    }

    if (tDeclaration->baseType & BT_StaticArray) {
        fprintf(fp, "%s[%.*s];\n", tDeclaration->name, tDeclaration->array->valLen, tDeclaration->array->val);
    } else if (tDeclaration->baseType & BT_DynArray) {
        fprintf(fp, "*%s;\n", tDeclaration->name);
    } else {
        fprintf(fp, "%s;\n", tDeclaration->name);
    }
}

static void PrintTypeFunction(AstNode node)
{
    AssertNodeKind(node, Declaration);
    if (tDeclaration->type->kind == NK_Expression) {
        AstExpression expr = (AstExpression) tDeclaration->type;
        switch (expr->tk) {
        case TK_ENUM:
            PrintEnumFunction(expr, tDeclaration);
            break;
        case TK_FIELD:
            PrintFieldFunction(expr, tDeclaration);
            break;
        case TK_STRUCT:
            PrintStructFunction(expr, tDeclaration);
            break;
        case TK_CHOSE:
            PrintChoseFunction(expr, tDeclaration);
            break;
        default:
            break;
        }
    }

    if (tDeclaration->ql == NULL && tDeclaration->funcName == NULL &&
        !(tDeclaration->baseType & (BT_StaticArray | BT_DynArray))) {
        AstExpression expr = (AstExpression) tDeclaration->type;
        if (expr->kind == NK_Label) {
            tDeclaration->funcName = ((AstLabel) tDeclaration->type)->name;
        } else if (expr->tk == TK_NUM) {
            tDeclaration->funcName = "Number";
        } else if (expr->tk == TK_DECIMAL) {
            tDeclaration->funcName = "Decimal";
        } else {
            tDeclaration->funcName = expr->funcName;
        }
        return;
    }

    const char *type;
    if (tDeclaration->funcName == NULL) {
        type = GetTypeString(tDeclaration, 0);
        tDeclaration->funcName = Print2Buf("DeclarationFunc%d", SoleIndex++);
    } else {
        type = tDeclaration->funcName;
    }
    if ((tDeclaration->baseType & BT_DynArray) && type != tDeclaration->funcName) {
        SPrint("void Pause%s(%s **val, char type) {\n", tDeclaration->funcName, type);
    } else if ((tDeclaration->baseType & BT_StaticArray) && type != tDeclaration->funcName) {
        SPrint("void Pause%s(%s (*val)[%.*s], char type) {\n", tDeclaration->funcName, type,
            tDeclaration->array->valLen, tDeclaration->array->val);
    } else {
        SPrint("void Pause%s(%s *val, char type) {\n", tDeclaration->funcName, type);
    }
    if (tDeclaration->ql != NULL && tDeclaration->ql->when != NULL) {
        SPrint("%sif (!(%.*s)) return;\n", Indent, tDeclaration->ql->when->valLen, tDeclaration->ql->when->val);
    }

    SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
    if ((tDeclaration->baseType & BT_DynArray) && type != tDeclaration->funcName) {
        SPrint("%s%s *valTmp;\n", Indent, type);
    } else if ((tDeclaration->baseType & BT_StaticArray) && type != tDeclaration->funcName) {
        SPrint("%s%s valTmp[%.*s];\n", Indent, type,
            tDeclaration->array->valLen, tDeclaration->array->val);
    } else {
        SPrint("%s%s valTmp;\n", Indent, type);
    }
    SPrint("%sif (val == NULL)\n", Indent);
    SPrint("%s%sval = &valTmp;\n", Indent, Indent);
    SPrint("%sint oldType = type;\n", Indent);
    SPrint("%stype = T_Read;\n", Indent);
    SPrint("%s#else // ifndef ReadAndPrintOnly\n", Indent);
    SPrint("%sCheckInput(val);\n", Indent);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);

    if (tDeclaration->baseType & BT_DynArray) {
        if (tDeclaration->array != NULL) {
            SPrint("%sint i, count = (%.*s);\n", Indent, tDeclaration->array->valLen, tDeclaration->array->val);
            SPrint("%sif (type == T_Read) {\n", Indent);
            SPrint("%s%s#ifdef ReadAndPrintOnly\n", Indent, Indent);
            SPrint("%s%s*val = ST_Alloc(sizeof(%s));\n", Indent, Indent, type);
            SPrint("%s%s#else // ifndef ReadAndPrintOnly\n", Indent, Indent);
            SPrint("%s%s*val = ST_Alloc(sizeof(%s) * count);\n", Indent, Indent, type);
            SPrint("%s%s#endif // ReadAndPrintOnly\n", Indent, Indent);
        } else {
            SPrint("%sint i, count;\n", Indent);
            SPrint("%sif (type == T_Read) {\n", Indent);
            SPrint("%s%scount = GetMaxCount(sizeof(%s));\n", Indent, Indent, type);
            SPrint("%s%s#ifdef ReadAndPrintOnly\n", Indent, Indent);
            SPrint("%s%s*val = ST_Alloc(sizeof(%s));\n", Indent, Indent, type);
            SPrint("%s%s#else // ifndef ReadAndPrintOnly\n", Indent, Indent);
            SPrint("%s%s*val = ST_Alloc(sizeof(%s) * count);\n", Indent, Indent, type);
            SPrint("%s%s#endif // ReadAndPrintOnly\n", Indent, Indent);
            SPrint("%s} else {\n", Indent);
            SPrint("%s%scount = GetBufferSize(*val) / sizeof(%s);\n", Indent, Indent, type);
        }
        SPrint("%s}\n", Indent);
        SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
        SPrint("%s#define ValPtr (*val)\n", Indent);
        SPrint("%s#else // ifndef ReadAndPrintOnly\n", Indent);
        SPrint("%s#define ValPtr (*val + i)\n", Indent);
        SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    } else if (tDeclaration->baseType & BT_StaticArray) {
        SPrint("%sint i, count = %.*s;\n", Indent, tDeclaration->array->valLen, tDeclaration->array->val);
        SPrint("%s#define ValPtr (*val + i)\n", Indent);
    } else {
        SPrint("%s#define ValPtr val\n", Indent);
    }

    if (tDeclaration->ql == NULL) {
        SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
        SPrint("%stype = oldType;\n", Indent);
        SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
        if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
            SPrint("%sPrintLevelAdd(type);\n", Indent);
            SPrint("%sfor (i = 0; i < count && Available(); i++) {\n", Indent);
            SPrint("%s%sPrintLabelKey(Number2Str(i), type);\n", Indent, Indent);
            SPrint("%s%s", Indent, Indent);
        } else {
            SPrint(Indent);
        }
        AstExpression expr = (AstExpression) tDeclaration->type;
        if (expr->kind == NK_Label) {
            SPrint("Pause%s(ValPtr, type);\n", ((AstLabel) tDeclaration->type)->name);
        } else if (expr->tk == TK_NUM) {
            SPrint("PauseNumber(ValPtr, type);\n");
        } else if (expr->tk == TK_DECIMAL) {
            SPrint("PauseDecimal(ValPtr, type);\n");
        } else {
            SPrint("Pause%s(ValPtr, type);\n", expr->funcName);
        }
        if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
            SPrint("%s%sPrintLabelEndKey(type);\n", Indent, Indent);
            SPrint("%s}\n", Indent);
            SPrint("%sPrintLevelSub(type);\n", Indent);
        }
        SPrint("%s#undef ValPtr\n", Indent);
        SPrint("}\n");
        return;
    }

    // 打印
    SPrint("%s#ifndef ReadAndPrintOnly\n", Indent);
    SPrint("%sif (type == T_Print) {\n", Indent);
    if (tDeclaration->ql->print != NULL) {
        SPrint("%s%s%.*s(val, T_Print);\n", Indent, Indent,
            tDeclaration->ql->print->valLen, tDeclaration->ql->print->val);
    } else {
        if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
            SPrint("%s%sPrintLevelAdd(T_Print);\n", Indent, Indent);
            SPrint("%s%sfor (i = 0; i < count; i++) {\n", Indent, Indent);
            SPrint("%s%s%sPrintLabelKey(Number2Str(i), T_Print);\n", Indent, Indent, Indent);
            SPrint("%s%s%s", Indent, Indent, Indent);
        } else {
            SPrint("%s%s", Indent, Indent);
        }

        AstExpression expr = (AstExpression) tDeclaration->type;
        if (expr->kind == NK_Label) {
            SPrint("Pause%s(ValPtr, T_Print);\n", ((AstLabel) tDeclaration->type)->name);
        } else if (expr->tk == TK_NUM) {
            SPrint("PauseNumber(ValPtr, T_Print);\n");
        } else if (expr->tk == TK_DECIMAL) {
            SPrint("PauseDecimal(ValPtr, T_Print);\n");
        } else {
            SPrint("Pause%s(ValPtr, T_Print);\n", expr->funcName);
        }

        if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
            SPrint("%s%s%sPrintLabelEndKey(T_Print);\n", Indent, Indent, Indent);
            SPrint("%s%s}\n", Indent, Indent);
            SPrint("%s%sPrintLevelSub(T_Print);\n", Indent, Indent);
        }
    }
    SPrint("%s%sreturn;\n%s}\n", Indent, Indent, Indent);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);

    // 属性设置
    if (tDeclaration->ql->pos != NULL) {
        SPrint("%sSetPos(%.*s);\n", Indent, tDeclaration->ql->pos->valLen, tDeclaration->ql->pos->val);
    }
    if (tDeclaration->ql->sort != 0) {
        SPrint("%sint sort = SetSort(%d);\n", Indent, tDeclaration->ql->sort);
    }
    if (tDeclaration->ql->sign != 0) {
        SPrint("%sint sign = SetSign(%d);\n", Indent, tDeclaration->ql->sign);
    }
    if (tDeclaration->ql->size != 0) {
        SPrint("%sSetSize(%d);\n", Indent, tDeclaration->ql->size);
    }
    if (tDeclaration->ql->align != 0) {
        SPrint("%sSetAlign(%d);\n", Indent, tDeclaration->ql->align);
    }
    if (tDeclaration->ql->len != 0) {
        SPrint("%slong long oldLen = InOut->len;\n", Indent);
        SPrint("%sInOut->len = InOut->pos + ", Indent);
        SPrint("(%.*s);\n", tDeclaration->ql->len->valLen, tDeclaration->ql->len->val);
        SPrint("%sif (InOut->len > oldLen) InOut->len = oldLen;\n", Indent);
        if ((tDeclaration->baseType & BT_DynArray) && tDeclaration->array == NULL) {
            SPrint("%sif (type == T_Read) {\n", Indent);
            SPrint("%s%scount = GetMaxCount(sizeof(%s));\n", Indent, Indent, type);
            SPrint("%s}\n", Indent);
        }
    }

    // 输入/输出
    if (tDeclaration->ql->export != NULL && (tDeclaration->baseType & (BT_StaticArray | BT_DynArray))) {
        SPrint("%sif (type == T_Read) ", Indent);
        SPrint("%s.oft = InOut->buf + InOut->pos;\n", tDeclaration->ql->export);
    }
    if (tDeclaration->ql->print == NULL) {
        SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
        SPrint("%stype = oldType;\n", Indent);
        SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    }
    if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
        SPrint("%sPrintLevelAdd(type);\n", Indent);
        SPrint("%sfor (i = 0; i < count && Available(); i++) {\n", Indent);
        SPrint("%s%sPrintLabelKey(Number2Str(i), type);\n%s", Indent, Indent, Indent);
    }
    if (tDeclaration->ql->decode == NULL && tDeclaration->ql->encode == NULL) {
        AstExpression expr = (AstExpression) tDeclaration->type;
        if (expr->kind == NK_Label) {
            SPrint("%sPause%s(ValPtr, type);\n", Indent, ((AstLabel) tDeclaration->type)->name);
        } else if (expr->tk == TK_NUM) {
            SPrint("%sPauseNumber(ValPtr, type);\n", Indent);
        } else if (expr->tk == TK_DECIMAL) {
            SPrint("%sPauseDecimal(ValPtr, type);\n", Indent);
        } else {
            SPrint("%sPause%s(ValPtr, type);\n", Indent, expr->funcName);
        }
    } else {
        SPrint("%s#ifndef ReadAndPrintOnly\n", Indent);
        SPrint("%sif (type == T_Read) {\n", Indent);
        SPrint("%s#endif // ReadAndPrintOnly\n%s%s", Indent, Indent, Indent);
        if (tDeclaration->ql->decode != NULL) {
            SPrint("%.*s(ValPtr, type);\n", tDeclaration->ql->decode->valLen, tDeclaration->ql->decode->val);
        } else {
            AstExpression expr = (AstExpression) tDeclaration->type;
            if (expr->kind == NK_Label) {
                SPrint("Pause%s(ValPtr, type);\n", ((AstLabel) tDeclaration->type)->name);
            } else if (expr->tk == TK_NUM) {
                SPrint("PauseNumber(ValPtr, type);\n");
            } else if (expr->tk == TK_DECIMAL) {
                SPrint("PauseDecimal(ValPtr, type);\n");
            } else {
                SPrint("Pause%s(ValPtr, type);\n", expr->funcName);
            }
        }
        SPrint("%s#ifndef ReadAndPrintOnly\n", Indent);
        SPrint("%s} else if (type == T_Write) {\n%s%s", Indent, Indent, Indent);
        if (tDeclaration->ql->encode != NULL) {
            SPrint("%.*s(ValPtr, T_Write);\n", tDeclaration->ql->encode->valLen, tDeclaration->ql->encode->val);
        } else {
            AstExpression expr = (AstExpression) tDeclaration->type;
            if (expr->kind == NK_Label) {
                SPrint("Pause%s(ValPtr, T_Write);\n", ((AstLabel) tDeclaration->type)->name);
            } else if (expr->tk == TK_NUM) {
                SPrint("WriteNumber(*ValPtr);\n");
            } else if (expr->tk == TK_DECIMAL) {
                SPrint("WriteDecimal(*ValPtr);\n");
            } else {
                SPrint("Pause%s(ValPtr, T_Write);\n", expr->funcName);
            }
        }
        SPrint("%s} else {\n", Indent);
        SPrint("%s%sThrowError(__LINE__);\n%s}\n", Indent, Indent, Indent);
        SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    }
    if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
        SPrint("%s%sPrintLabelEndKey(type);\n", Indent, Indent);
        SPrint("%s}\n", Indent);
        SPrint("%sPrintLevelSub(type);\n", Indent);
    }
    if (tDeclaration->ql->print != NULL) {
        SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
        SPrint("%s%.*s(val, oldType);\n", Indent,
            tDeclaration->ql->print->valLen, tDeclaration->ql->print->val);
        SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    }
    if (tDeclaration->ql->export != NULL) {
        SPrint("%s#ifndef ReadAndPrintOnly\n", Indent);
        SPrint("%sif (type == T_Read)\n", Indent);
        SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
        if (tDeclaration->baseType & (BT_StaticArray | BT_DynArray)) {
            SPrint("%s%s.len = InOut->buf + InOut->pos - (unsigned char*) %s.oft;\n",
                   Indent, tDeclaration->ql->export, tDeclaration->ql->export);
        } else {
            SPrint("%s%s = *val;\n", Indent, tDeclaration->ql->export);
        }
        AddOutputVariable(tDeclaration->ql->export, tDeclaration);
    }
    SPrint("%s#undef ValPtr\n", Indent);

    // 属性还原
    if (tDeclaration->ql->align != 0) {
        SPrint("%sSetAlign(%d);\n", Indent, tDeclaration->ql->align);
    }
    if (tDeclaration->ql->sort != 0) {
        SPrint("%sSetSort(sort);\n", Indent);
    }
    if (tDeclaration->ql->sign != 0) {
        SPrint("%sSetSign(sign);\n", Indent);
    }
    if (tDeclaration->ql->size != 0) {
        SPrint("%sSetSize(0);\n", Indent);
    }
    if (tDeclaration->ql->len != 0) {
        SPrint("%sInOut->pos = InOut->len;\n", Indent);
        SPrint("%sInOut->len = oldLen;\n", Indent);
    }
    SPrint("}\n");
}

/*********************************************************/

// 在C源文件中输出类型定义的解析函数
static void PrintTypedefExpression(AstNode node)
{
    AssertNodeKind(node, Declaration);
    if (tDeclaration->ql == NULL && tDeclaration->type->kind == NK_Expression &&
        !(tDeclaration->baseType & (BT_StaticArray | BT_DynArray))) {
        AstExpression expr = (AstExpression) tDeclaration->type;
        switch (expr->tk) {
        case TK_ENUM:
            expr->funcName = tDeclaration->name;
            PrintEnumFunction(expr, tDeclaration);
            break;
        case TK_FIELD:
            expr->funcName = tDeclaration->name;
            PrintFieldFunction(expr, tDeclaration);
            break;
        case TK_STRUCT:
            expr->funcName = tDeclaration->name;
            PrintStructFunction(expr, tDeclaration);
            break;
        case TK_CHOSE:
            expr->funcName = tDeclaration->name;
            PrintChoseFunction(expr, tDeclaration);
            break;
        default:
            tDeclaration->funcName = tDeclaration->name;
            PrintTypeFunction(node);
            break;
        }
    } else {
        tDeclaration->funcName = tDeclaration->name;
        PrintTypeFunction(node);
    }

    AddOutputFunction(tDeclaration->name);
}

// 在C源文件中包含模块头文件
static void PrintImportExpression(AstNode node)
{
    AssertNodeKind(node, Literal);
    PrintLineNumber(tLiteral->line, CHeaderFile);
    HPrint("#include \"%.*s.h\"\n", tLiteral->valLen, tLiteral->val);
}

// 在C源文件中输出Stream源文件包含的源码
static void PrintCompileExpression(AstNode node)
{
    AssertNodeKind(node, Literal);
    PrintLineNumber(tLiteral->line, CSourceFile);
    SPrint("%.*s\n", tLiteral->valLen, tLiteral->val);
}

// 在C源文件中输出根节点解析函数
static void PrintRootNode(AstNode node)
{
    PrintTypeFunction(node);

    AssertNodeKind(node, Declaration);
    SPrint("void PauseAll(void *buf, int type)\n{\n");
    SPrint("%s#ifdef ReadAndPrintOnly\n", Indent);
    SPrint("%sPause%s(NULL, T_Print);\n", Indent, tDeclaration->funcName);

    SPrint("%s#else // ifndef ReadAndPrintOnly\n", Indent);
    const char *type = GetTypeString(tDeclaration, 0);
    if (tDeclaration->baseType & BT_DynArray) {
        type = Print2Buf("%s **", type);
    } else if (tDeclaration->baseType & BT_StaticArray) {
        type = Print2Buf("%s (*)[%.*s]", type, tDeclaration->array->valLen, tDeclaration->array->val);
    } else {
        type = Print2Buf("%s *", type);
    }
    SPrint("%sPause%s((%s) buf, type);\n", Indent, tDeclaration->funcName, type);
    SPrint("%s#endif // ReadAndPrintOnly\n", Indent);
    SPrint("}\n");
}

// 输出C源文件
void PrintCode(AstNode root, const char *outPath, const char *fileName)
{
    OpenOutputFile(outPath != NULL ? outPath : ".", fileName);
    // 输出结构定义
    while (root != NULL) {
        SPrint("\n");
        HPrint("\n");
        if (root->kind == NK_Expression) {
            AstExpression expr = (AstExpression) root;
            switch (expr->tk) {
            case TK_TYPEDEF:
                PrintTypedefExpression(expr->kids[0]);
                HPrint("typedef ");
                PrintTypeDefine(expr->kids[0], 0, CHeaderFile);
                break;
            case TK_IMPORT:
                PrintImportExpression(expr->kids[0]);
                break;
            case TK_COMPILE:
                PrintCompileExpression(expr->kids[0]);
                break;
            default:
                PrintRootNode(root);
                break;
            }
        } else {
            PrintRootNode(root);
        }
        root = root->next;
    }

    // 输出全局变量定义
    int i;
    SPrint("\n// Global variable definition, count=%d\n", OutputVariableCount);
    HPrint("\n// Global variable declaration, count=%d\n", OutputVariableCount);
    for (i = 0; i < OutputVariableCount; i++) {
        SPrint("%s %s;\n", OutputVariable[i].type, OutputVariable[i].name);
        HPrint("extern %s %s;\n", OutputVariable[i].type, OutputVariable[i].name);
    }

    // 类型解析函数声明
    HPrint("\n// Type function declaration, count=%d\n", OutputFunctionCount);
    for (i = 0; i < OutputFunctionCount; i++) {
        HPrint("void Pause%s(%s *val, char type);\n", OutputFunction[i], OutputFunction[i]);
    }

    CloseOuputFile();
}
